package com.passwordmanager.service.impl;

import com.passwordmanager.dto.auth.AuthResponse;
import com.passwordmanager.dto.auth.LoginRequest;
import com.passwordmanager.dto.auth.RegisterRequest;
import com.passwordmanager.model.User;
import com.passwordmanager.repository.UserRepository;
import com.passwordmanager.security.JwtTokenProvider;
import com.passwordmanager.service.AuthService;
import dev.samstevens.totp.code.*;
import dev.samstevens.totp.exceptions.QrGenerationException;
import dev.samstevens.totp.qr.QrData;
import dev.samstevens.totp.qr.QrGenerator;
import dev.samstevens.totp.qr.ZxingPngQrGenerator;
import dev.samstevens.totp.secret.DefaultSecretGenerator;
import dev.samstevens.totp.time.SystemTimeProvider;
import dev.samstevens.totp.time.TimeProvider;
import dev.samstevens.totp.util.Utils;
import lombok.RequiredArgsConstructor;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 认证服务实现类
 * 提供用户注册、登录、MFA认证等功能
 */
@Service
@RequiredArgsConstructor
public class AuthServiceImpl implements AuthService {

    private final UserRepository userRepository;
    private final PasswordEncoder passwordEncoder;
    private final JwtTokenProvider tokenProvider;
    private final AuthenticationManager authenticationManager;

    /**
     * 用户注册
     * @param request 注册请求，包含用户名、密码、邮箱等信息
     * @return 认证响应，包含JWT令牌
     */
    @Override
    @Transactional
    public AuthResponse register(RegisterRequest request) {
        // 检查用户名是否已存在
        if (userRepository.existsByUsername(request.getUsername())) {
            throw new RuntimeException("用户名已存在");
        }

        // 检查邮箱是否已被注册
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new RuntimeException("邮箱已被注册");
        }

        // 创建新用户
        User user = new User();
        user.setUsername(request.getUsername());
        user.setEmail(request.getEmail());
        user.setPassword(passwordEncoder.encode(request.getPassword()));
        user.setPhoneNumber(request.getPhoneNumber());

        userRepository.save(user);

        // 进行身份认证
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = tokenProvider.generateToken(authentication);
        
        return AuthResponse.builder()
                .token(jwt)
                .username(user.getUsername())
                .mfaEnabled(false)
                .requiresMfaSetup(false)
                .build();
    }

    /**
     * 用户登录
     * @param request 登录请求，包含用户名和密码
     * @return 认证响应，包含JWT令牌或MFA状态
     */
    @Override
    public AuthResponse login(LoginRequest request) {
        // 验证用户名和密码
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(request.getUsername(), request.getPassword())
        );

        User user = userRepository.findByUsername(request.getUsername())
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        // 如果启用了MFA，需要验证MFA代码
        if (user.isMfaEnabled()) {
            if (request.getMfaCode() == null) {
                return AuthResponse.builder()
                        .username(user.getUsername())
                        .mfaEnabled(true)
                        .requiresMfaSetup(false)
                        .build();
            }
            
            if (!validateMfaCode(user.getMfaSecret(), request.getMfaCode())) {
                throw new RuntimeException("MFA验证码无效");
            }
        }

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = tokenProvider.generateToken(authentication);
        
        return AuthResponse.builder()
                .token(jwt)
                .username(user.getUsername())
                .mfaEnabled(user.isMfaEnabled())
                .requiresMfaSetup(false)
                .build();
    }

    /**
     * 验证MFA代码
     * @param username 用户名
     * @param mfaCode MFA验证码
     * @return 认证响应，包含JWT令牌
     */
    @Override
    public AuthResponse verifyMfaCode(String username, String mfaCode) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        if (!validateMfaCode(user.getMfaSecret(), mfaCode)) {
            throw new RuntimeException("MFA验证码无效");
        }

        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        String jwt = tokenProvider.generateToken(authentication);

        return AuthResponse.builder()
                .token(jwt)
                .username(username)
                .mfaEnabled(true)
                .requiresMfaSetup(false)
                .build();
    }

    /**
     * 生成MFA二维码
     * @param username 用户名
     * @return 二维码图片的Base64编码
     */
    @Override
    public String generateMfaQrCode(String username) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        // 生成MFA密钥
        DefaultSecretGenerator secretGenerator = new DefaultSecretGenerator();
        String secret = secretGenerator.generate();

        // 创建二维码数据
        QrData data = new QrData.Builder()
                .label(username)
                .secret(secret)
                .issuer("Password Manager")
                .algorithm(HashingAlgorithm.SHA1)
                .digits(6)
                .period(30)
                .build();

        // 生成二维码图片
        QrGenerator generator = new ZxingPngQrGenerator();
        byte[] imageData;
        try {
            imageData = generator.generate(data);
        } catch (QrGenerationException e) {
            throw new RuntimeException("生成QR码失败", e);
        }

        String qrCodeImage = Utils.getDataUriForImage(imageData, generator.getImageMimeType());

        // 保存MFA密钥
        user.setMfaSecret(secret);
        userRepository.save(user);

        return qrCodeImage;
    }

    /**
     * 启用MFA
     * @param username 用户名
     * @param mfaCode MFA验证码
     * @return 是否启用成功
     */
    @Override
    @Transactional
    public boolean enableMfa(String username, String mfaCode) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        if (!validateMfaCode(user.getMfaSecret(), mfaCode)) {
            throw new RuntimeException("MFA验证码无效");
        }

        user.setMfaEnabled(true);
        userRepository.save(user);
        return true;
    }

    /**
     * 禁用MFA
     * @param username 用户名
     * @param mfaCode MFA验证码
     * @return 是否禁用成功
     */
    @Override
    @Transactional
    public boolean disableMfa(String username, String mfaCode) {
        User user = userRepository.findByUsername(username)
                .orElseThrow(() -> new RuntimeException("用户不存在"));

        if (!validateMfaCode(user.getMfaSecret(), mfaCode)) {
            throw new RuntimeException("MFA验证码无效");
        }

        user.setMfaEnabled(false);
        user.setMfaSecret(null);
        userRepository.save(user);
        return true;
    }

    /**
     * 验证MFA代码
     * @param secret MFA密钥
     * @param code MFA验证码
     * @return 是否验证成功
     */
    private boolean validateMfaCode(String secret, String code) {
        if (secret == null || code == null) {
            return false;
        }

        TimeProvider timeProvider = new SystemTimeProvider();
        CodeGenerator codeGenerator = new DefaultCodeGenerator();
        CodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
        return verifier.isValidCode(secret, code);
    }
} 